/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | foam-extend: Open Source CFD
   \\    /   O peration     | Version:     4.1
    \\  /    A nd           | Web:         http://www.foam-extend.org
     \\/     M anipulation  | For copyright notice see file Copyright
-------------------------------------------------------------------------------
License
	This file is part of foam-extend.

	foam-extend is free software: you can redistribute it and/or modify it
	under the terms of the GNU General Public License as published by the
	Free Software Foundation, either version 3 of the License, or (at your
	option) any later version.

	foam-extend is distributed in the hope that it will be useful, but
	WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
	General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with foam-extend.  If not, see <http://www.gnu.org/licenses/>.

\*---------------------------------------------------------------------------*/

#include "objectRegistry.H"
#include "foamTime.H"

// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //

defineTypeNameAndDebug(Foam::objectRegistry, 0);


// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //

bool Foam::objectRegistry::parentNotTime() const
{
	return (&parent_ != dynamic_cast<const objectRegistry*>(&time_));
}


// * * * * * * * * * * * * * * * * Constructors *  * * * * * * * * * * * * * //

Foam::objectRegistry::objectRegistry
(
	const Time& t,
	const label nIoObjects
)
:
	regIOobject
	(
		IOobject
		(
			string::validate<word>(t.caseName()),
			t,
			IOobject::NO_READ,
			IOobject::AUTO_WRITE
		),
		true    // to flag that this is the top-level regIOobject
	),
	HashTable<regIOobject*>(nIoObjects),
	time_(t),
	parent_(t),
	dbDir_(name()),
	event_(1)
{}


Foam::objectRegistry::objectRegistry
(
	const IOobject& io,
	const label nIoObjects
)
:
	regIOobject(io),
	HashTable<regIOobject*>(nIoObjects),
	time_(io.time()),
	parent_(io.db()),
	dbDir_(parent_.dbDir()/local()/name()),
	event_(1)
{
	writeOpt() = IOobject::AUTO_WRITE;
}


Foam::objectRegistry::objectRegistry
(
	const IOobject& io,
	const fileName& dbDir,
	const label nIoObjects
)
:
	regIOobject(io),
	HashTable<regIOobject*>(nIoObjects),
	time_(io.time()),
	parent_(io.db()),
	dbDir_(dbDir),
	event_(1)
{
	writeOpt() = IOobject::AUTO_WRITE;
}


// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //

Foam::objectRegistry::~objectRegistry()
{
	List<regIOobject*> myObjects(size());
	label nMyObjects = 0;

	for (iterator iter = begin(); iter != end(); ++iter)
	{
		if (iter()->ownedByRegistry())
		{
			myObjects[nMyObjects++] = iter();
		}
		else
		{
			// HR 29.11.17: Object needs to be marked a unregistered.
			// Overwise regIOobject::checkOut uses the object registry
			// we are deleting
			iter()->deregister();
		}
	}

	for (label i=0; i<nMyObjects; i++)
	{
		checkOut(*myObjects[i]);
	}
}


// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //

const Foam::Time& Foam::objectRegistry::time() const
{
	return time_;
}


Foam::wordList Foam::objectRegistry::names() const
{
	return HashTable<regIOobject*>::toc();
}


Foam::wordList Foam::objectRegistry::sortedNames() const
{
	return HashTable<regIOobject*>::sortedToc();
}


Foam::wordList Foam::objectRegistry::names(const word& ClassName) const
{
	wordList objectNames(size());

	label count=0;
	for (const_iterator iter = cbegin(); iter != cend(); ++iter)
	{
		if (iter()->type() == ClassName)
		{
			objectNames[count++] = iter.key();
		}
	}

	objectNames.setSize(count);

	return objectNames;
}


Foam::wordList Foam::objectRegistry::sortedNames(const word& ClassName) const
{
	wordList sortedLst = names(ClassName);
	sort(sortedLst);

	return sortedLst;
}


Foam::fileName Foam::objectRegistry::mangleFileName
(
	const fileName& fName
) const
{
	return fName;
}


const Foam::objectRegistry& Foam::objectRegistry::subRegistry
(
	const word& name,
	const bool forceCreate
) const
{
	if (forceCreate && !foundObject<objectRegistry>(name))
	{
		objectRegistry* fieldsCachePtr = new objectRegistry
		(
			IOobject
			(
				name,
				time().constant(),
				*this,
				IOobject::NO_READ,
				IOobject::NO_WRITE
			)
		);
		fieldsCachePtr->store();
	}
	return lookupObject<objectRegistry>(name);
}


bool Foam::objectRegistry::found(const word& name) const
{
	return HashTable<regIOobject*>::found(name);
}


Foam::label Foam::objectRegistry::getEvent() const
{
	label curEvent = event_++;

	if (event_ == labelMax)
	{
		WarningInFunction
			<< "Event counter has overflowed. "
			<< "Resetting counter on all dependent objects." << nl
			<< "This might cause extra evaluations." << endl;

		// Reset event counter
		curEvent = 1;
		event_ = 2;

		for (const_iterator iter = begin(); iter != end(); ++iter)
		{
			const regIOobject& io = *iter();

			if (objectRegistry::debug)
			{
				Pout<< "objectRegistry::getEvent() : "
					<< "resetting count on " << iter.key() << endl;
			}

			if (io.eventNo() != 0)
			{
				const_cast<regIOobject&>(io).eventNo() = curEvent;
			}
		}
	}

	return curEvent;
}


bool Foam::objectRegistry::checkIn(regIOobject& io) const
{
	if (objectRegistry::debug)
	{
		Pout<< "objectRegistry::checkIn(regIOobject&) : "
			<< name() << " : checking in " << io.name()
			<< " of type " << io.type()
			<< endl;
	}

	return const_cast<objectRegistry&>(*this).insert(io.name(), &io);
}


bool Foam::objectRegistry::checkOut(regIOobject& io) const
{
	iterator iter = const_cast<objectRegistry&>(*this).find(io.name());

	if (iter != end())
	{
		if (objectRegistry::debug)
		{
			Pout<< "objectRegistry::checkOut(regIOobject&) : "
				<< name() << " : checking out " << iter.key()
				<< endl;
		}

		if (iter() != &io)
		{
			if (objectRegistry::debug)
			{
				WarningInFunction
					<< name() << " : attempt to checkOut copy of "
					<< iter.key()
					<< endl;
			}

			return false;
		}
		else
		{
			regIOobject* object = iter();

			bool hasErased = const_cast<objectRegistry&>(*this).erase(iter);

			if (io.ownedByRegistry())
			{
				delete object;
			}

			return hasErased;
		}
	}
	else
	{
		if (objectRegistry::debug)
		{
			Pout<< "objectRegistry::checkOut(regIOobject&) : "
				<< name() << " : could not find " << io.name()
				<< " in registry " << name()
				<< endl;
		}
	}

	return false;
}


void Foam::objectRegistry::rename(const word& newName)
{
	regIOobject::rename(newName);

	// adjust dbDir_ as well
	string::size_type i = dbDir_.rfind('/');

	if (i == string::npos)
	{
		dbDir_ = newName;
	}
	else
	{
		dbDir_.replace(i+1, string::npos, newName);
	}
}


bool Foam::objectRegistry::modified() const
{
	forAllConstIter(HashTable<regIOobject*>, *this, iter)
	{
		if (iter()->modified())
		{
			return true;
		}
	}

	return false;
}


void Foam::objectRegistry::readModifiedObjects()
{
	for (iterator iter = begin(); iter != end(); ++iter)
	{
		if (objectRegistry::debug)
		{
			Pout<< "objectRegistry::readModifiedObjects() : "
				<< name() << " : Considering reading object "
				<< iter.key() << endl;
		}

		iter()->readIfModified();
	}
}


bool Foam::objectRegistry::readIfModified()
{
	readModifiedObjects();
	return true;
}


bool Foam::objectRegistry::writeObject
(
	IOstream::streamFormat fmt,
	IOstream::versionNumber ver,
	IOstream::compressionType cmp
) const
{
	bool ok = true;

	forAllConstIter(HashTable<regIOobject*>, *this, iter)
	{
		if (objectRegistry::debug)
		{
			Pout<< "objectRegistry::write() : "
				<< name() << " : Considering writing object "
				<< iter.key()
				<< " of type " << iter()->type()
				<< " with writeOpt " << iter()->writeOpt()
				<< " to file " << iter()->objectPath()
				<< endl;
		}

		if (iter()->writeOpt() != NO_WRITE)
		{
			ok = iter()->writeObject(fmt, ver, cmp) && ok;
		}
	}

	return ok;
}


// ************************************************************************* //
